package com.yugao.fintech.framework.lock.model;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.yugao.fintech.framework.assistant.core.exception.Assert;
import com.yugao.fintech.framework.lock.exception.LockException;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

public class CuratorLock extends LockTemplate {
    private static final Logger log = LoggerFactory.getLogger(CuratorLock.class);

    private final CuratorFramework curatorFramework;
    private final TransmittableThreadLocal<InterProcessLock> interProcessMutexTtl = new TransmittableThreadLocal<>();

    public CuratorLock(String lockKey, CuratorFramework curatorFramework) {
        super(lockKey);
        Assert.notNull(curatorFramework, "curatorFramework is null");
        this.curatorFramework = curatorFramework;
    }

    @Override
    public void unlock() {
        InterProcessLock lock = interProcessMutexTtl.get();
        if (lock != null) {
            try {
                lock.release();
            } catch (Exception e) {
                log.error("unlock fail, error: ", e);
            }
        }
    }

    @Override
    public void lock() {
        InterProcessLock lock = interProcessMutexTtl.get();
        if (lock == null) {
            lock = new InterProcessMutex(curatorFramework, this.lockKey);
            interProcessMutexTtl.set(lock);
        }
        try {
            lock.acquire();
        } catch (Exception e) {
            log.error("try get lock fail, msg: {}", e.getMessage());
            throw new LockException("get lock fail");
        }
    }

    @Override
    public boolean isLocked() {
        InterProcessLock lock = interProcessMutexTtl.get();
        if (lock == null) {
            lock = new InterProcessMutex(curatorFramework, this.lockKey);
            interProcessMutexTtl.set(lock);
        }
        return lock.isAcquiredInThisProcess();
    }

    @Override
    public boolean tryLock(long waitTime, TimeUnit unit) {
        InterProcessLock lock = interProcessMutexTtl.get();
        if (lock == null) {
            lock = new InterProcessMutex(curatorFramework, this.lockKey);
            interProcessMutexTtl.set(lock);
        }
        try {
            lock.acquire(waitTime, unit);
            return true;
        } catch (Exception e) {
            log.error("try get lock fail, error: ", e);
        }
        return false;
    }
}
